#pragma once


namespace fb
{
	class Vec3;

	class Vec3
	{
	public:
		float x, y, z, w;
		Vec3(void); 
		Vec3(float X, float Y, float Z);
		void Init(float ix=0.0f, float iy=0.0f, float iz=0.0f);
		bool IsValid() const;
		float operator[](int i) const;
		float& operator[](int i);
		inline void Zero();
		bool operator==(const Vec3& v) const;
		bool operator!=(const Vec3& v) const;	
		Vec3&	operator+=(const Vec3 &v);			
		Vec3&	operator-=(const Vec3 &v);		
		Vec3&	operator*=(const Vec3 &v);			
		Vec3&	operator*=(float s);
		Vec3&	operator/=(const Vec3 &v);		
		Vec3&	operator/=(float s);	
		Vec3&	operator+=(float fl);
		Vec3&	operator-=(float fl);
		inline float	Length() const;
		__forceinline float LengthSqr(void) const
		{
			return (x*x + y*y + z*z);		
		}
		__forceinline bool IsZero( float tolerance = 0.001f ) const
		{
			return (x > -tolerance && x < tolerance &&
				y > -tolerance && y < tolerance &&
				z > -tolerance && z < tolerance);
		}
		float	NormalizeInPlace();
		float	DistToSqr(const Vec3 &vOther) const;
		float	Dot(const Vec3& vOther) const;			
		float	Length2D(void) const;		
		float	Length2DSqr(void) const;
		Vec3& operator=(const Vec3 &vOther);
		Vec3	operator-(void) const;
		Vec3	operator+(const Vec3& v) const;	
		Vec3	operator-(const Vec3& v) const;	
		Vec3	operator*(const Vec3& v) const;	
		Vec3	operator/(const Vec3& v) const;	
		Vec3	operator*(float fl) const;
		Vec3	operator/(float fl) const;
	};
}


namespace fb
{
	//===============================================
	void Vec3::Init( float ix, float iy, float iz )    
	{ 
		x = ix; y = iy; z = iz;
	}
	//===============================================
	Vec3::Vec3(float X, float Y, float Z)
	{ 
		x = X; y = Y; z = Z;
	}
	//===============================================
	Vec3::Vec3(void){ }
	//===============================================
	void Vec3::Zero()
	{
		x = y = z = 0.0f;
	}
	//===============================================
	void Vec3Clear( Vec3& a )
	{
		a.x = a.y = a.z = 0.0f;
	}
	//===============================================
	Vec3& Vec3::operator=(const Vec3 &vOther)	
	{
		x=vOther.x; y=vOther.y; z=vOther.z; 
		return *this; 
	}
	//===============================================
	float& Vec3::operator[](int i)
	{
		return ((float*)this)[i];
	}
	//===============================================
	float Vec3::operator[](int i) const
	{
		return ((float*)this)[i];
	}
	//===============================================
	bool Vec3::operator==( const Vec3& src ) const
	{
		return (src.x == x) && (src.y == y) && (src.z == z);
	}
	//===============================================
	bool Vec3::operator!=( const Vec3& src ) const
	{
		return (src.x != x) || (src.y != y) || (src.z != z);
	}
	//===============================================
	void Vec3Copy( const Vec3& src, Vec3& dst )
	{
		dst.x = src.x;
		dst.y = src.y;
		dst.z = src.z;
	}
	//===============================================
	Vec3& Vec3::operator+=(const Vec3& v)	
	{ 
		x+=v.x; y+=v.y; z += v.z;	
		return *this;
	}
	//===============================================
	Vec3& Vec3::operator-=(const Vec3& v)	
	{ 
		x-=v.x; y-=v.y; z -= v.z;	
		return *this;
	}
	//===============================================
	Vec3& Vec3::operator*=(float fl)	
	{
		x *= fl;
		y *= fl;
		z *= fl;
		return *this;
	}
	//===============================================
	Vec3& Vec3::operator*=(const Vec3& v)	
	{
		x *= v.x;
		y *= v.y;
		z *= v.z;
		return *this;
	}
	//===============================================
	Vec3&	Vec3::operator+=(float fl) 
	{
		x += fl;
		y += fl;
		z += fl;
		return *this;
	}
	//===============================================
	Vec3&	Vec3::operator-=(float fl) 
	{
		x -= fl;
		y -= fl;
		z -= fl;
		return *this;
	}
	//===============================================
	Vec3& Vec3::operator/=(float fl)	
	{
		float oofl = 1.0f / fl;
		x *= oofl;
		y *= oofl;
		z *= oofl;
		return *this;
	}
	//===============================================
	Vec3& Vec3::operator/=(const Vec3& v)	
	{ 
		x /= v.x;
		y /= v.y;
		z /= v.z;
		return *this;
	}
	//===============================================
	float Vec3::Length(void) const	
	{
		float root = 0.0f;

		float sqsr = x*x + y*y + z*z;

		__asm
		{
			sqrtss xmm0, sqsr
				movss root, xmm0
		}

		return root;
	}
	//===============================================
	float Vec3::Length2D(void) const
	{
		float root = 0.0f;

		float sqst = x*x + y*y;

		__asm
		{
			sqrtss xmm0, sqst
				movss root, xmm0
		}

		return root;
	}
	//===============================================
	float Vec3::Length2DSqr(void) const
	{ 
		return (x*x + y*y); 
	}
	//===============================================
	Vec3 CrossProduct(const Vec3& a, const Vec3& b) 
	{ 
		return Vec3( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); 
	}
	//===============================================
	Vec3 CrossProduct(const Vec3& a, const Vec3& b, Vec3& v) 
	{ 
		v=Vec3( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); 
		return v;
	}
	//===============================================
	float Vec3::DistToSqr(const Vec3 &vOther) const
	{
		Vec3 delta;

		delta.x = x - vOther.x;
		delta.y = y - vOther.y;
		delta.z = z - vOther.z;

		return delta.LengthSqr();
	}
	//===============================================
	float Vec3::NormalizeInPlace()
	{
		Vec3& v = *this;

		float iradius = 1.f / ( this->Length() ); //FLT_EPSILON

		v.x *= iradius;
		v.y *= iradius;
		v.z *= iradius;
		return 1.f/iradius;
	}
	//===============================================
	Vec3 Vec3::operator+(const Vec3& v) const	
	{ 
		Vec3 res;
		res.x = x + v.x;
		res.y = y + v.y;
		res.z = z + v.z;
		return res;	
	}
	//===============================================
	Vec3 Vec3::operator-(const Vec3& v) const	
	{ 
		Vec3 res;
		res.x = x - v.x;
		res.y = y - v.y;
		res.z = z - v.z;
		return res;	
	}
	//===============================================
	Vec3 Vec3::operator*(float fl) const	
	{ 
		Vec3 res;
		res.x = x * fl;
		res.y = y * fl;
		res.z = z * fl;
		return res;	
	}
	//===============================================
	Vec3 Vec3::operator*(const Vec3& v) const	
	{ 
		Vec3 res;
		res.x = x * v.x;
		res.y = y * v.y;
		res.z = z * v.z;
		return res;	
	}
	//===============================================
	Vec3 Vec3::operator/(float fl) const	
	{ 
		Vec3 res;
		res.x = x / fl;
		res.y = y / fl;
		res.z = z / fl;
		return res;	
	}
	//===============================================
	Vec3 Vec3::operator/(const Vec3& v) const	
	{ 
		Vec3 res;
		res.x = x / v.x;
		res.y = y / v.y;
		res.z = z / v.z;
		return res;
	}
	float Vec3::Dot( const Vec3& vOther ) const
	{
		const Vec3& a = *this;

		return( a.x*vOther.x + a.y*vOther.y + a.z*vOther.z ); 
	}
}